package com.letoken.platform.app.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.firebase.database.annotations.NotNull;
import com.letoken.platform.app.service.AppUserWalletService;
import com.letoken.platform.pub.constant.LetokenUserConstant;
import com.letoken.platform.pub.constant.RedisKeyConstant;
import com.letoken.platform.pub.enums.LetokenApiResponseEnum;
import com.letoken.platform.pub.enums.LetokenTokenTypeEnum;
import com.letoken.platform.pub.enums.TokenTransactionStatusEnum;
import com.letoken.platform.pub.enums.TokenTransactionTypeEnum;
import com.letoken.platform.pub.exception.ApiServiceException;
import com.letoken.platform.repo.admin.dao.WithdrawMapper;
import com.letoken.platform.repo.admin.po.Withdraw;
import com.letoken.platform.repo.app.dao.AppUserWalletMapper;
import com.letoken.platform.repo.app.manager.AppUserAssetsHistoryManager;
import com.letoken.platform.repo.app.manager.TokenManager;
import com.letoken.platform.repo.app.po.AppUserAssetsHistory;
import com.letoken.platform.repo.app.po.AppUserWallet;
import com.letoken.platform.repo.app.po.Token;
import com.letoken.platform.repo.app.redis.RedisConfig;
import com.letoken.platform.repo.utlis.LocalBlockUtils;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * Le账户-用户资产表 服务实现类
 * </p>
 *
 * @author 作者
 * @since 2022-12-10
 */
@Service
@Slf4j
public class AppUserWalletServiceImpl extends ServiceImpl<AppUserWalletMapper, AppUserWallet> implements AppUserWalletService {

    @Resource
    private AppUserWalletMapper appUserWalletMapper;

    @Autowired
    private WithdrawMapper withdrawMapper;

    @Autowired
    private TokenManager tokenManager;

    @Autowired
    private AppUserAssetsHistoryManager appUserAssetsHistoryManager;

//    @Autowired
//    private AppUserAddressMapper appUserAddressMapper;

    @Resource
    private RedisConfig redisConfig;


    /**
     * 修改用户金额 减传负数
     *
     * @param availableBalance 可用金额
     * @param freezeBalance    冻结金额
     * @param releasedBalance  等待释放金额
     * @param userId           用户id
     * @param tokenId          币种id
     * @return
     */
    @Override
    public int updateUserAmountByUserIDandCoin(BigDecimal availableBalance, BigDecimal freezeBalance, BigDecimal releasedBalance, Integer userId, Integer tokenId) {
        String key = String.format(RedisKeyConstant.UPDATE_WALLET_INFO,userId);
        int rulest;
        RedissonClient redissonClient = redisConfig.getRedisson();
        RLock rlock = redissonClient.getLock(key);
        try {
            rlock.lock(5,TimeUnit.SECONDS);
            QueryWrapper<AppUserWallet> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("user_id", userId).eq("token_id", tokenId);
            AppUserWallet userWallet = appUserWalletMapper.selectOne(queryWrapper);
            if (userWallet.getAvailableBalance().add(availableBalance).compareTo(BigDecimal.ZERO) < 0) {
                log.error(MessageFormat.format("流通余额{0}少于扣减金额{1}", userWallet.getAvailableBalance(), availableBalance));
                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_APP_ASSETS_002);
            }
            if (userWallet.getFreezeBalance().add(freezeBalance).compareTo(BigDecimal.ZERO) < 0) {
                log.error(MessageFormat.format("冻结余额{0}少于扣减金额{1}", userWallet.getFreezeBalance(), freezeBalance));
                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_APP_ASSETS_002);
            }
            if (userWallet.getReleasedBalance().add(releasedBalance).compareTo(BigDecimal.ZERO) < 0) {
                log.error(MessageFormat.format("待释放余额{0}少于扣减金额{1}", userWallet.getReleasedBalance(), releasedBalance));
                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_APP_ASSETS_002);
            }
            rulest = appUserWalletMapper.updateUserAmountByUserIDandCoin(availableBalance, freezeBalance, releasedBalance, userWallet.getVersion(), userId, tokenId);
        } finally {
            if (rlock != null && rlock.isHeldByCurrentThread()) {
                rlock.unlock();
            }
        }
        return rulest;
    }

    @Override
    public int updateWithdrawInternalOrder(Withdraw record, TokenTransactionStatusEnum status) {
        record.setConfirms(0);
        record.setProcessTime(LocalDateTime.now());
        record.setStatus(status.getValue());
        record.setResponse("");
        int state = withdrawMapper.updateById(record);


        // 修改转出用户余额
        if(status == TokenTransactionStatusEnum.COMPLETED) {
            int balanceState = updateUserAmountByUserIDandCoin(record.getAmount().negate(), BigDecimal.valueOf(0),BigDecimal.valueOf(0), record.getUserId(), record.getTokenId());
            if (balanceState == 0) {
                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_006);
            }
        }
        // 修改转入用户余额
        if(status == TokenTransactionStatusEnum.COMPLETED) {
            int balanceState = updateUserAmountByUserIDandCoin(record.getAmount(), BigDecimal.valueOf(0), BigDecimal.valueOf(0), record.getToId(), record.getTokenId());
            if (balanceState == 0) {
                throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_006);
            }
        }

        // 修改转出用户资产记录
        int historyOutState = updateUserAssetsHistory(record, record.getUserId(), record.getAmount().negate(), TokenTransactionTypeEnum.WITHDRAW);
        if (historyOutState == 0) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_007);
        }
        // 修改转入用户资产记录
        int historyInState = updateUserAssetsHistory(record, record.getToId(), record.getAmount(), TokenTransactionTypeEnum.RECHARGE);
        if (historyInState == 0) {
            throw new ApiServiceException(LetokenApiResponseEnum.ERROR_WALLET_TX_007);
        }

        return state;
    }


    public int updateUserAssetsHistory(@NotNull Withdraw record, Integer userId, BigDecimal amount, TokenTransactionTypeEnum type) {
        Token token = tokenManager.queryById(record.getTokenId());
        AppUserAssetsHistory data = appUserAssetsHistoryManager.queryBySn(userId, record.getSn());
        if (data == null) {
            data = new AppUserAssetsHistory();
            data.setType(type.getValue());
            data.setUserId(userId);
            data.setTokenId(record.getTokenId());
            data.setTokenCurrency(token.getTokenCurrency());
            if(org.springframework.util.StringUtils.isEmpty(data.getTxid())) {
                data.setTxid(record.getTxid());
            }
            data.setSn(record.getSn());
            data.setStatus(record.getStatus());
            data.setCreateTime(LocalDateTime.now());
            data.setAmount(amount);

            try {
                data.setBalance(appUserWalletMapper.getAvailableBalance(record.getTokenId(), userId));
            }catch (Exception e){
                log.error("查询用户可用余额失败 withdraw sn {} eMsg{}", record.getSn(), e.getMessage());
            }


            LocalBlockUtils.AddressPo po = LocalBlockUtils.obtainAddress(LocalBlockUtils.ObtainAddressDto.builder()
                    .userId(record.getUserId())
                    .coinId(LetokenTokenTypeEnum.USDT.getId())
                    .build());
//            String fromAddress = appUserAddressMapper.queryAddressByUserId(record.getUserId());
            data.setFromUserName("le"+record.getUserId().toString());
            data.setFromAddress(po.getAddr());
            String toAddress = record.getAddress();
            if (StringUtils.startsWithIgnoreCase(toAddress, LetokenUserConstant.USER_ID_PREFIX)){
                String addressUserId = org.apache.commons.lang.StringUtils.substring(toAddress, 2);
                if (!addressUserId.equals(userId.toString())){
                    data.setToAddress(toAddress);
                }else {
//                    toAddress = appUserAddressMapper.queryAddressByUserId(Integer.parseInt(addressUserId));

                    po = LocalBlockUtils.obtainAddress(LocalBlockUtils.ObtainAddressDto.builder()
                            .userId(Integer.parseInt(addressUserId))
                            .coinId(LetokenTokenTypeEnum.USDT.getId())
                            .build());
                    toAddress = po.getAddr();

                    data.setToAddress(toAddress);
                }

            }else {
                data.setToAddress(toAddress);
            }

            return appUserAssetsHistoryManager.add(data);
        }
        else {
            if(StringUtils.isEmpty(data.getTxid())) {
                data.setTxid(record.getTxid());
            }
            data.setUpdateTime(LocalDateTime.now());
            data.setStatus(record.getStatus());
            return appUserAssetsHistoryManager.update(data);
        }
    }


    @Override
    @Transactional
    public  void updateWallet(Integer coinId, Integer userId, BigDecimal amount, Integer type,String seq) {
        log.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~"+"开始执行修改钱包数据  业务类型:"+type+"用户id:"+userId+"操作金额:"+amount+"开始时间"+LocalDateTime.now()+"~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        String key = String.format(RedisKeyConstant.UPDATE_WALLET_INFO,userId);
        RedissonClient redissonClient = redisConfig.getRedisson();
        RLock rlock = redissonClient.getLock(key);
        LocalDateTime localDateTime = LocalDateTime.now();
        try {
            rlock.lock();
            Integer walletId = appUserWalletMapper.checkoutTokenIdAndUserId(coinId, userId);
            AppUserWallet w = appUserWalletMapper.selectForUpdate(walletId);
            log.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~"+"业务类型:"+type+"用户id:"+userId+"操作金额:"+amount+"查询到的金额:"+w.getAvailableBalance()+"当前时间:"+localDateTime);
            AppUserWallet updateW = new AppUserWallet();
            updateW.setId(w.getId());
            updateW.setAvailableBalance(w.getAvailableBalance().add(amount));
            appUserWalletMapper.updateById(updateW);
           // TransactionAspectSupport.currentTransactionStatus().flush();


            AppUserAssetsHistory assetHistory = new AppUserAssetsHistory();
            assetHistory.setUserId(userId);
            assetHistory.setType(type);
            assetHistory.setSn(seq);
            assetHistory.setStatus(TokenTransactionStatusEnum.COMPLETED.getValue());
            assetHistory.setTokenId(coinId);
            assetHistory.setAmount(amount);
            assetHistory.setBalance(updateW.getAvailableBalance());
            assetHistory.setCreateTime(localDateTime);
            assetHistory.setUpdateTime(localDateTime);
            assetHistory.insert();
            log.info("~~~~~~~~~~~~~~~~~~~~~~~~~~~业务类型:"+type+"用户id:"+userId+"操作金额:"+amount+"修改后的金额:"+updateW.getAvailableBalance()+"执行完成时间当前时间:"+LocalDateTime.now()+"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
        }catch (Exception e){
            log.info("修改金额失败!"+"业务类型:"+type+"用户id:"+userId+"操作金额:"+amount+"当前时间:"+localDateTime+"  错误信息:"+e.getMessage());
            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
        }finally {
            if (rlock != null && rlock.isHeldByCurrentThread()) {
                rlock.unlock();
            }
        }
    }
}
